﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using MonoStrategy.RenderSystem;
using System.IO;

namespace MonoStrategy
{
    class BuildingGridState
    {
        public PositionTracker Position;
        public RenderableVisual Visual;
        public BuildingConfig Config;
        public int ShiftX;
        public int ShiftY;
    }

    public partial class Program 
    {
        private static BuildingGridState m_BuildingGrid;
        private static Timer m_GUILoaderTimer;

        public static GLRenderer Renderer { get; private set; }
        public static TerrainRenderer TerrainRenderer { get { return Renderer.TerrainRenderer; } }
        public static GameMap Map { get; private set; }
        public static long CurrentCycle { get { return Map.CurrentCycle; } }
        public static XMLConfig Config { get;  set; }
        public static readonly Object GUILoadLock = new Object();


        public static void Initialize() 
        {

            // setup renderer
            Renderer = new GLRenderer(Config.GLRenderer);

            Renderer.OnRenderSprites += (unused) =>
            {
                lock (GUILoadLock)
                {
                    var layout = GUILayout;

                    if (layout == null) 
                        return;

                    layout.RootElement.Render();
                }
            };
            Renderer.OnMouseMove += (unused, mouse) =>
            {
                lock (GUILoadLock)
                {
                    var layout = GUILayout;

                    if (layout == null)
                        return;

                    layout.RootElement.ProcessMouseMoveEvent(mouse.X, mouse.Y);
                }
            };
            Renderer.OnMouseDown += (unused, btn) =>
            {
                lock (GUILoadLock)
                {
                    var layout = GUILayout;

                    if (layout == null)
                        return;

                    if (layout.RootElement.ProcessMouseDownEvent(Renderer.MouseXY.X, Renderer.MouseXY.Y, btn))
                        return;

                    if (btn == Control.LeftMouseButton)
                    {
                        var buildingGrid = m_BuildingGrid;

                        if (buildingGrid != null)
                        {
                            Map.AddBuilding(buildingGrid.Config, buildingGrid.Position.Position.ToPoint());
                        }
                        else
                        {
                            Terrain_OnSelectObject(TerrainRenderer.MouseOverVisual);
                        }
                    }
                    else if (btn == Control.RightMouseButton)
                    {
                        HideBuildingGrid();
                        BuildingInspector.Abort();
                    }
                }
            };
            Renderer.OnMouseUp += (unused, btn) =>
            {
                lock (GUILoadLock)
                {
                    var layout = GUILayout;

                    if (layout == null)
                        return;

                    layout.RootElement.ProcessMouseUpEvent(Renderer.MouseXY.X, Renderer.MouseXY.Y, btn);
                }
            };
            Renderer.OnKeyDown += Renderer_OnKeyDown;
            Renderer.OnKeyRepeat += Renderer_OnKeyRepeat;

            AnimationLibrary.OpenFromDirectory(GetResourcePath("AnimationLibrary"));

            InitializeGame();
        }

        private static void Terrain_OnSelectObject(RenderableVisual inObject)
        {
            GUI.ShowObjectInspector(inObject);
        }

        private static void InitializeGame()
        {
            Map = new GameMap(512, 59, new RaceConfig[] { RaceConfigLoader.Open(GetResourcePath("RaceConfig/Roman.Buildings.xml")) });

            Renderer.AttachTerrain(Map.Terrain);

            m_GUILoaderTimer = new Timer((unused) => { UpdateGUIConfig(); }, null, 1000, 1000);

            Map.OnRemoveBuilding += (unused, obj) => { VisualBuilding.Detach(obj); };
            Map.OnAddBuilding += (unused, obj) => { VisualBuilding.Assign(TerrainRenderer, obj); };
            Map.OnAddMovable += (unused, obj) => { VisualMovable.Assign(TerrainRenderer, obj); };
            Map.OnRemoveMovable += (unused, obj) => { VisualMovable.Detach(obj); };
            Map.OnAddStack += (unused, obj) => { VisualStack.Assign(TerrainRenderer, obj); };
            Map.OnRemoveStack += (unused, obj) => { VisualStack.Detach(obj); };
            Map.OnAddBuildTask += (unused, obj) => { VisualBuildTask.Assign(TerrainRenderer, obj); ShowBuildingGrid(obj.Config); };
            Map.OnRemoveBuildTask += (unused, obj) => { VisualBuildTask.Detach(obj); };
            Map.OnAddFoilage += (unused, obj) => { VisualFoilage.Assign(TerrainRenderer, obj); };
            Map.OnRemoveFoilage += (unused, obj) => { VisualFoilage.Detach(obj); };
            Map.OnAddStone += (unused, obj) => { VisualStone.Assign(TerrainRenderer, obj); };
            Map.OnRemoveStone += (unused, obj) => { VisualStone.Detach(obj); };

            TerrainRenderer.PrecisionTimerCallback = () => { return Map.AnimationTime; };

            TerrainRenderer.OnMouseGridMove += (sender, gridPos) =>
            {
                if (m_BuildingGrid != null)
                {
                    // show building
                    gridPos.X -= m_BuildingGrid.Config.GridWidth / 2;
                    gridPos.Y -= m_BuildingGrid.Config.GridHeight / 2;

                    if (gridPos.X < 0)
                        gridPos.X = 0;

                    if (gridPos.Y < 0)
                        gridPos.Y = 0;

                    m_BuildingGrid.Position.Position = CyclePoint.FromGrid(gridPos);
                }
            };

            // initialize map with random seed
            Map.GenerateMapFromFile(GetResourcePath("Maps/User/Alien.xml"), 0);

            // add some initial stacks and movables
            Point spot = Map.Terrain.Spots.First();

            Map.Start(() =>
            {
                Point start = spot;

                for (int x = 0; x < 3; x++)
                {
                    for (int y = 0; y < 3; y++)
                    {
                        Map.AddMovable(CyclePoint.FromGrid(start.X + x, start.Y + y - 10), MovableType.Grader);
                        Map.AddMovable(CyclePoint.FromGrid(start.X + x + 5, start.Y + y - 10), MovableType.Constructor);
                    }
                }

                for (int x = 0; x < 3; x++)
                {
                    for (int y = 0; y < 3; y++)
                    {
                        Map.AddMovable(CyclePoint.FromGrid(start.X + x, start.Y + y), MovableType.Settler);
                        Map.AddMovable(CyclePoint.FromGrid(start.X + x + 5, start.Y + y), MovableType.Settler);
                        Map.AddMovable(CyclePoint.FromGrid(start.X + x, start.Y + y + 5), MovableType.Settler);
                        Map.AddMovable(CyclePoint.FromGrid(start.X + x + 5, start.Y + y + 5), MovableType.Settler);
                    }
                }


                Map.DropResource(new Point(start.X, start.Y), Resource.Timber, 30);
                Map.DropResource(new Point(start.X, start.Y), Resource.Stone, 31);
                Map.DropResource(new Point(start.X, start.Y), Resource.Coal, 26);
                Map.DropResource(new Point(start.X, start.Y), Resource.IronOre, 12);
                Map.DropResource(new Point(start.X, start.Y), Resource.Hammer, 28);
                Map.DropResource(new Point(start.X, start.Y), Resource.Shovel, 15);
                Map.DropResource(new Point(start.X, start.Y), Resource.Axe, 8);
                Map.DropResource(new Point(start.X, start.Y), Resource.Saw, 3);
                Map.DropResource(new Point(start.X, start.Y), Resource.PickAxe, 11);
                Map.DropResource(new Point(start.X, start.Y), Resource.Hook, 2);
                Map.DropResource(new Point(start.X, start.Y), Resource.Scythe, 3);
                Map.DropResource(new Point(start.X, start.Y), Resource.Fish, 7);
                Map.DropResource(new Point(start.X, start.Y), Resource.Meat, 8);
                Map.DropResource(new Point(start.X, start.Y), Resource.Bread, 15);
            });

            Renderer.EnableTerrainRendering = true;
            TerrainRenderer.ScreenXY = new PointDouble(spot.X - 20, (spot.Y - 50) / Math.Sqrt(2));

            Map.ClearLog();
        }

        static void Renderer_OnKeyRepeat(GLRenderer inSender, OpenTK.Input.Key inButton)
        {
            double diff = 1;

            switch (inButton)
            {
                case OpenTK.Input.Key.Right: TerrainRenderer.ScreenXY = new PointDouble(TerrainRenderer.ScreenXY.X + diff, TerrainRenderer.ScreenXY.Y); break;
                case OpenTK.Input.Key.Left: TerrainRenderer.ScreenXY = new PointDouble(TerrainRenderer.ScreenXY.X - diff, TerrainRenderer.ScreenXY.Y); break;
                case OpenTK.Input.Key.Down: TerrainRenderer.ScreenXY = new PointDouble(TerrainRenderer.ScreenXY.X, TerrainRenderer.ScreenXY.Y + diff); break;
                case OpenTK.Input.Key.Up: TerrainRenderer.ScreenXY = new PointDouble(TerrainRenderer.ScreenXY.X, TerrainRenderer.ScreenXY.Y - diff); break;
            }
        }

        static void Renderer_OnKeyDown(GLRenderer inSender, OpenTK.Input.Key inButton)
        {
            switch (inButton)
            {
                case OpenTK.Input.Key.F12: Map.ForwardOneMinute(); break;
                case OpenTK.Input.Key.S:
                    {
                        // save game state
                        Map.SuspendGame();

                        try
                        {
                            Program.StoreGameState(false);
                        }
                        finally
                        {
                            Map.ResumeGame();
                        }

                    }; break;
                case OpenTK.Input.Key.P: if (Map.IsSuspended) Map.ResumeGame(); else Map.SuspendGame(); break;
                case OpenTK.Input.Key.R: Renderer.EnableTerrainRendering = !Renderer.EnableTerrainRendering; break;
            }
        }

        public static String GetResourcePath(String inResource)
        {
            return Config.ResourcePath + "/" + inResource;
        }

       

        private static void ShowBuildingGrid(BuildingConfig inConfig)
        {
            HideBuildingGrid();

            m_BuildingGrid = new BuildingGridState()
            {
                Config = inConfig,
                Position = new PositionTracker(),
                ShiftX = inConfig.GridWidth / 2,
                ShiftY = (int)((inConfig.GridHeight * 1 / Math.Sqrt(2)) / 2),
            };

            Map.Terrain.ResetBuildingGrid(inConfig);
            m_BuildingGrid.Visual = TerrainRenderer.CreateAnimation(inConfig.AnimationClass, m_BuildingGrid.Position, 0.6);

            TerrainRenderer.IsBuildingGridVisible = true;
        }


        private static void HideBuildingGrid()
        {
            TerrainRenderer.IsBuildingGridVisible = false;

            if (m_BuildingGrid == null)
                return;

            TerrainRenderer.RemoveVisual(m_BuildingGrid.Visual);

            m_BuildingGrid = null;
        }

        

    }
}
